//https://blog.csdn.net/as480133937/article/details/97765912
//https://blog.csdn.net/u010173859/article/details/9904441

#include "lcd.h"

u16 data[512];

 /*! 
 *  @brief us定时
 */

void for_delay_us(uint32_t nus)
{
    uint32_t Delay = nus * 72/4;
    do
    {
        __NOP();
    }
    while (Delay --);
}

/*! 
 *  @brief LCD串行发送一个字节
 */
void SendByte(u8 byte)
{
     u8 i; 
	  for(i = 0;i < 8;i++)
    {
        if((byte << i) & 0x80)  //0x80(1000 0000)  只会保留最高位
		{
            SID_1;
		}
		else
		{
            SID_0;
		}
        SCLK_0;
        for_delay_us(5);
        SCLK_1;
	}   
}
 
/*! 
 *  @brief      LCD写指令
 */
void Lcd_WriteCmd(u8 Cmd )
{
    HAL_Delay(1);//由于我们没有写LCD正忙的检测，所以直接延时1ms，使每次写入数据或指令间隔大于1ms 便可不用写忙状态检测
     SendByte(WRITE_CMD);            //11111,RW(0),RS(0),0   
     SendByte(0xf0&Cmd);      //高四位
     SendByte(Cmd<<4);   //低四位(先执行<<)
}
 
/*! 
 *  @brief      LCD写数据
 */
void Lcd_WriteData(u8 Dat )
{
    HAL_Delay(1);    //由于我们没有写LCD正忙的检测，所以直接延时1ms，使每次写入数据或指令间隔大于1ms 便可不用写忙状态检测
     SendByte(WRITE_DAT);            //11111,RW(0),RS(1),0
     SendByte(0xf0&Dat);      //高四位
     SendByte(Dat<<4);   //低四位(先执行<<)
}

/*! 
 *  @brief      LCD初始化
 */
void Lcd_Init(void)
{ 
    HAL_Delay(50);  	//等待液晶自检（延时>40ms）
	Lcd_WriteCmd(0x30);        //功能设定:选择基本指令集  ，选择8bit数据流
    //延时>137us
    HAL_Delay(1); 
    Lcd_WriteCmd(0x0c);        //开显示
    //延时>100us
    HAL_Delay(1);
    Lcd_WriteCmd(0x01);        //清除显示，并且设定地址指针为00H
    HAL_Delay(30);
    //延时>10ms
    Lcd_WriteCmd(0x02);
    HAL_Delay(30);
    Lcd_WriteCmd(0x06);
    HAL_Delay(30);
    Lcd_WriteCmd(0x80);
    HAL_Delay(30);
}

/*! 
 *  @brief      清屏函数
 */
void LCD_Clear(void)
	{
		Lcd_WriteCmd(0x01);			//清屏指令
        HAL_Delay(2);//延时以待液晶稳定【至少1.6ms】
	}

/* 字符显示RAM地址    4行8列 */
uint8_t LCD_addr[4][8]={
	{0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87},  		//第一行
	{0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97},		//第二行
	{0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F},		//第三行
	{0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F}		//第四行
	};
 
/*! 
 *  @brief      显示字符或汉字
 */
void LCD_Display_Words(uint8_t x,uint8_t y,char*str)
{ 
	Lcd_WriteCmd(LCD_addr[x][y]); //写初始光标位置
	while(*str>0)
    { 
      Lcd_WriteData(*str);    //写数据
      str++;     
    }
}    

void LCD_Display_Number(uint8_t x,uint8_t y,int num)
{
    char i[2];
    i[0] = num /10 +48;
    i[1] = num %10 +48;
    LCD_Display_Words(x,y,i);
}

u16 Exchange(u8 xx)				  //自定义一个转换函数
{
	u16 data;
	if(xx==0)	data=0x8000;
	if(xx==1)	data=0x4000;	
	if(xx==2)	data=0x2000;	
	if(xx==3)	data=0x1000;	
	if(xx==4)	data=0x0800;	
	if(xx==5)	data=0x0400;	
	if(xx==6)	data=0x0200;	
	if(xx==7)	data=0x0100;
		
	if(xx==8)	data=0x0080;	
	if(xx==9)	data=0x0040;	
	if(xx==10)	data=0x0020;	
	if(xx==11)	data=0x0010;	
	if(xx==12)	data=0x0008;	
	if(xx==13)	data=0x0004;	
	if(xx==14)	data=0x0002;	
	if(xx==15)	data=0x0001;	
	
	return data;		
}

void LCD_Clear_Screen(void)   	//清屏操作
{
	  u8 i,j;
	  Lcd_WriteCmd(0x34);//打开拓展指令集，绘图指令关
	  for(j=0;j<8;j++)    //清除第一行
	  {
	    for(i=0;i<32;i++)
	    {
	      Lcd_WriteCmd(0x80+i);//行
	      Lcd_WriteCmd(0x80+j);//列
	      Lcd_WriteData(0x00);//写高八位数据
	      Lcd_WriteData(0x00);//写低八位数据
	    }
	  }
 
	  for(j=0;j<8;j++)    //清除第二行
	  {
	    for(i=0;i<32;i++)
	    {
	     Lcd_WriteCmd(0x80+i);
	     Lcd_WriteCmd(0x88+j);
	      Lcd_WriteData(0x00);
	      Lcd_WriteData(0x00);
	    }
	   }
	 Lcd_WriteCmd(0x30);//打开基本指令集
}


void LCD_Draw_Point(u8 x,u8 xx,u8 y)		 //画小点，在X列的第xx个小列的第y行中画一个小点
{
	 data[y+x*32] |= Exchange(xx);				   
}

void LCD_Display(uint8_t x)
{
    uint8_t i;
    Lcd_WriteCmd(0x34);
    for ( i = 0; i < 32; i++)
    {
        Lcd_WriteCmd(0x80 + i);
        Lcd_WriteCmd(0x80 + x);
        Lcd_WriteData((data[i+x*32] >> 8) & 0xff);
        Lcd_WriteData(data[i+x*32] & 0xff);
    }
    Lcd_WriteCmd(0x36);
    Lcd_WriteCmd(0x30);
}

void LCD_Draw_Line(uint8_t line[])
{
	int i,y,x,xx;
	for(i = 0; i <128; i++)
	{
		x = i/16;
		xx = i%16;
		if(line[i] <32 )
		{
			x = x+8;
			y = 32 - line[i];
			LCD_Draw_Point(x,xx,y);
		}
	}
}
